<template>
  <div class="container">
    <h1>📜 动态消息列表</h1>

    <!-- 使用 vue-virtual-scroller 创建高性能虚拟列表 -->
    <DynamicScroller :items="items" :min-item-size="60" class="scroller">
      <template #default="{ item, index, active }">
        <DynamicScrollerItem
          :item="item"
          :active="active"
          :size-dependencies="[item.message]"
          :data-index="index"
        >
          <div class="item">
            <!-- 用户头像 -->
            <img :src="item.avatar" class="avatar" />

            <div class="content">
              <!-- 用户名称和时间戳 -->
              <div class="header">
                <span class="name">用户 {{ item.name }}</span>
                <span class="timestamp">{{ item.time }}</span>
              </div>

              <!-- 消息文本 -->
              <p class="message">{{ item.message }}</p>

              <!-- 动态配图（可选） -->
              <img v-if="item.image" :src="item.image" class="attachment" alt="动态配图" />

              <!-- 点赞和评论数 -->
              <div class="actions">❤️ {{ item.likes }} · 💬 {{ item.comments }}</div>

              <!-- 展开互动按钮 -->
              <button class="expand-btn" @click="toggleExpand(item.id)">⬇ 展开互动</button>

              <!-- 互动区：表情、附文、图片 -->
              <transition
                name="expand-height"
                @before-enter="beforeEnter"
                @enter="enter"
                @before-leave="beforeLeave"
                @leave="leave"
              >
                <div v-if="isExpanded(item.id)" class="emoji-toolbar">
                  😊 👍 🎉 🔁 💌
                  <div class="footer-text">{{ item.footerText }}</div>
                  <div class="footer-images">
                    <img
                      v-for="(img, idx) in item.footerImages"
                      :key="idx"
                      :src="img"
                      class="footer-img"
                    />
                  </div>
                </div>
              </transition>
            </div>
          </div>
        </DynamicScrollerItem>
      </template>
    </DynamicScroller>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'

/**
 * 单条动态消息数据结构
 */
interface Item {
  id: number
  avatar: string
  name: string
  time: string
  message: string
  image?: string
  likes: number
  comments: number
  footerText?: string
  footerImages?: string[]
}

/**
 * 生成固定几张种子图片（备用）
 */
const randomImages = Array.from(
  { length: 5 },
  (_, i) => `https://picsum.photos/seed/${i + 1}/${300 + i * 30}/${180 + i * 20}`,
)

/**
 * 生成一条随机消息文本
 * @returns 随机生成的消息内容
 */
function generateMessage(): string {
  const length = Math.floor(Math.random() * (300 - 10 + 1)) + 10
  const base =
    '生活中总有一些值得记录的瞬间，比如晴天的午后、一杯好咖啡、或是一句朋友的暖心话。Vue 让开发变得更优雅，构建界面也像写诗一样流畅。'
  let result = ''
  while (result.length < length) {
    result += base.slice(0, length - result.length)
  }
  return result
}

/**
 * 生成 footer 的附加文字描述
 * @returns 随机长度的附文内容
 */
function generateFooterText(): string {
  const length = Math.floor(Math.random() * (100 - 20 + 1)) + 20
  const base = 'Vue 真的是太优雅了，开发体验太棒了，组件化设计让项目维护更轻松。'
  let result = ''
  while (result.length < length) {
    result += base.slice(0, length - result.length)
  }
  return result
}

/**
 * 生成 footer 区域的随机图片
 * @returns 图片 URL 数组，每张图片尺寸不一
 */
function generateFooterImages(): string[] {
  const count = Math.floor(Math.random() * (5 - 3 + 1)) + 3
  return Array.from({ length: count }, () => {
    const width = Math.floor(Math.random() * (160 - 80 + 1)) + 80
    const height = Math.floor(Math.random() * (100 - 60 + 1)) + 60
    const seed = Math.floor(Math.random() * 10000)
    return `https://picsum.photos/seed/footer-${seed}/${width}/${height}`
  })
}

/**
 * 生成主图（动态配图）URL，尺寸随机
 * @returns 图片 URL
 */
function generateRandomImage(): string {
  const width = Math.floor(Math.random() * (360 - 240 + 1)) + 240
  const height = Math.floor(Math.random() * (220 - 120 + 1)) + 120
  const seed = Math.floor(Math.random() * 1000)
  return `https://picsum.photos/seed/${seed}/${width}/${height}`
}

/**
 * 所有动态消息项列表（虚拟滚动使用）
 */
const items = ref<Item[]>(
  Array.from({ length: 200 }, (_, i) => ({
    id: i + 1,
    avatar: `https://i.pravatar.cc/48?u=${i}`,
    name: `小明${i + 1}`,
    time: `发布于 ${new Date(Date.now() - i * 3600 * 1000).toLocaleString()}`,
    message: generateMessage(),
    footerText: generateFooterText(),
    footerImages: generateFooterImages(),
    image: generateRandomImage(),
    likes: Math.floor(Math.random() * 500),
    comments: Math.floor(Math.random() * 100),
  })),
)

/**
 * 展开状态的消息 ID 列表
 */
const expandedIds = ref<number[]>([])

/**
 * 切换指定消息项的互动展开状态
 * @param id 消息项 ID
 */
function toggleExpand(id: number) {
  const index = expandedIds.value.indexOf(id)
  if (index === -1) {
    expandedIds.value.push(id)
  } else {
    expandedIds.value.splice(index, 1)
  }
}

/**
 * 判断某项是否处于展开状态
 * @param id 消息项 ID
 * @returns 是否展开
 */
function isExpanded(id: number): boolean {
  return expandedIds.value.includes(id)
}

/**
 * 动画入场前：设置初始样式
 * @param el DOM 元素
 */
function beforeEnter(el: Element) {
  const target = el as HTMLElement
  target.style.maxHeight = '0'
  target.style.opacity = '0'
  target.style.overflow = 'hidden'
}

/**
 * 入场动画：展开区域
 * @param el DOM 元素
 * @param done 回调函数
 */
function enter(el: Element, done: () => void) {
  const target = el as HTMLElement
  const height = target.scrollHeight + 'px'
  target.style.transition = 'max-height 0.4s ease, opacity 0.4s ease'
  requestAnimationFrame(() => {
    target.style.maxHeight = height
    target.style.opacity = '1'
  })
  setTimeout(() => {
    target.style.maxHeight = 'none'
    done()
  }, 400)
}

/**
 * 离场前的准备样式
 * @param el DOM 元素
 */
function beforeLeave(el: Element) {
  const target = el as HTMLElement
  target.style.height = target.scrollHeight + 'px'
  target.style.overflow = 'hidden'
}

/**
 * 离场动画执行
 * @param el DOM 元素
 * @param done 回调函数
 */
function leave(el: Element, done: () => void) {
  const target = el as HTMLElement
  target.style.transition = 'height 0.4s ease, opacity 0.4s ease'
  requestAnimationFrame(() => {
    target.style.height = '0'
    target.style.opacity = '0'
  })
  setTimeout(() => {
    requestAnimationFrame(() => {
      done()
    })
  }, 300)
}
</script>

<style scoped>
.header {
  display: flex;
  justify-content: space-between;
  font-size: 0.85rem;
  color: #666;
  margin-bottom: 0.25rem;
}

.attachment {
  max-width: 100%;
  margin-top: 0.5rem;
  border-radius: 6px;
}

.actions {
  font-size: 0.8rem;
  color: #999;
  margin-top: 0.4rem;
}

.container {
  max-width: 600px;
  margin: auto;
  padding: 1rem;
}

.scroller {
  height: 80vh;
  overflow-y: auto;
  border: 1px solid #ccc;
  padding: 0.5rem;
  background-color: #f9f9f9;
}

.item {
  display: flex;
  align-items: flex-start;
  padding: 0.5rem 0;
  border-bottom: 1px solid #e5e5e5;
}

.avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  margin-right: 0.75rem;
}

.content {
  flex: 1;
}

.name {
  font-weight: bold;
  margin-bottom: 0.25rem;
}

.message {
  word-break: break-word;
  line-height: 1.4;
}

.expand-btn {
  margin-top: 0.5rem;
  background: #eee;
  border: none;
  padding: 0.3rem 0.6rem;
  font-size: 0.8rem;
  cursor: pointer;
  border-radius: 4px;
}

.emoji-toolbar {
  margin-top: 0.4rem;
  font-size: 1.5rem;
  padding: 0.4rem 0;
  text-align: center;
  overflow: hidden;
}

.fade-slide-enter-active,
.fade-slide-leave-active {
  transition:
    opacity 0.35s ease,
    transform 0.35s cubic-bezier(0.25, 1, 0.5, 1),
    scale 0.35s ease;
}

.fade-slide-enter-from,
.fade-slide-leave-to {
  opacity: 0;
  transform: translateY(10px) scale(0.9);
}

@keyframes bounceIn {
  0% {
    transform: scale(0.8);
    opacity: 0;
  }
  60% {
    transform: scale(1.05);
    opacity: 1;
  }
  100% {
    transform: scale(1);
  }
}

.content {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.footer-text {
  margin-top: 0.6rem;
  font-size: 0.85rem;
  line-height: 1.4;
  color: #333;
  padding: 0 0.5rem;
}

.footer-images {
  display: flex;
  gap: 6px;
  margin-top: 0.4rem;
  padding: 0 0.5rem;
  flex-wrap: wrap;
  justify-content: center;
}

.footer-img {
  width: 60px;
  height: 40px;
  object-fit: cover;
  border-radius: 4px;
}
</style>
